# !/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project ：prod_ctrl_service 
@File    ：input_raw.py
@IDE     ：PyCharm 
@Author  ：孔令伟
@Date    ：8/27/24 8:52 AM 
"""
import re
from decimal import Decimal
from typing import Dict, Union, Tuple

from mg_app_framework import MesCode, get_logger

from common.basic_mongo import (get_batch_code_config_collection, get_feeding_config_collection,
                                get_new_batch_code_config_collection)
from prod_ctrl_service.tool.tool import (check_basic_param, get_material_code_num_map, get_time, device_bom,
                                         device_workorder, send_http_msg, get_material_code_num_map_new)

"""
生产前-原料批次投料-投入当前生产所需要的原料条码(IF020)
设备生产启动前，MES系统验证原材料合法性通过后（重码校验、BOM所需）、登记当前投料的批次码以及数量，用于扣减逻辑
"""


async def input_raw_check(params: Dict[str, Union[str, Decimal]]) -> Dict[str, Union[Dict, str]]:
    """
    设备生产启动前，MES系统验证原材料合法性通过后（重码校验、BOM所需）、登记当前投料的批次码以及数量，用于扣减逻辑
    :param params:{
               "call_id":"JQ00386",  #接口调用方认证ID, string
               "trace_id":"8941d16b-0581-4e68-8940-a651afff4282",  #接口调用ID, string
               "upperpc_code": "UP011",    #上位机编码, string
               "user_code":"Y001",  #员工编码, string
               "equip_code":"ZP001",  # 设备编码, string
               "process_code":"2050",   # 工序编码, string, 必输
               "lot_no":"HQ2403190549088054230",  # 原料唯一批次码, string
               "qty":"500"    #投料数量, decimal
            }
    :return:{
               "code": "success",   #接口应答状态代码, success：成功; fail：失败, string
               "data": {}",  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
               "info": "OK"   #接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
            }
    """
    # '接口调用方认证ID/接口调用ID/上位机编码/员工编码/设备编码'是每一个接口都有且必填的入参，检查这些参数是否填写
    error_msg = ""
    basic_param_check_result, basic_param_check_message = check_basic_param(params=params)
    if not basic_param_check_result:
        error_msg += basic_param_check_message

    lot_no, qty = params.get("lot_no"), params.get("qty")
    if not lot_no or not qty:
        error_msg += "传入参数'原料惟一批次码(lot_no) or 投料数量(qty)'未填写;"

    if error_msg:
        result = {
            "code": MesCode.fail,  # 接口应答状态代码, success：成功; fail：失败, string
            "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
            "info": error_msg,  # 接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
        }
        return result

    process_code, equip_code = params.get("process_code"), params.get("equip_code")

    # 校验原材料批次码是否导入到MES中;校验原材料批次码对应的物料的数量是否大于投料数量
    # 校验与当前订单工序BOM的匹配性;原材料批次码配置de物料信息
    lot_no_check_result, lot_no_check_msg, material_code, lot_no, qty \
        = await check_code_exists(lot_no, qty, equip_code)
    if not lot_no_check_result:
        error_msg += lot_no_check_msg

    if error_msg:
        result = {
            "code": MesCode.fail,  # 接口应答状态代码, success：成功; fail：失败, string
            "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
            "info": error_msg,  # 接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
        }
        return result

    # todo Marking校验, marking相关功能未开发，等待开发后补充marking校验功能

    work_order = await device_workorder(device_code=equip_code)

    data = {
        "call_id": params.get("call_id"),
        "trace_id": params.get("trace_id"),
        "upperpc_code": params.get("upperpc_code"),
        "user_code": params.get("user_code"),
        "equip_code": params.get("equip_code"),
        "process_code": params.get("process_code"),
        "lot_no": params.get("lot_no"),
        "qty": params.get("qty"),
        "input_qty": params.get("qty"),  # 此字段用于记录本次投料的投料数据，后续原材料扣减不对这个字段进行更新
        "material_code": material_code,
        "is_feeding": True,
        "work_order": work_order,
        "time": get_time()
    }
    await update_and_insert_data(lot_no=lot_no, qty=qty, data=data)

    result = {
        "code": MesCode.success,  # 接口应答状态代码, success：成功; fail：失败, string
        "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
        "info": "OK",  # 接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
    }
    return result


async def check_code_exists(lot_no, qty, equip_code) -> Tuple[bool, str, str, str, float]:
    """
    校验原材料批次码是否导入到MES中;校验原材料批次码对应的物料的数量是否大于投料数量，这样才能扣减
    :param equip_code:
    :param qty:
    :param lot_no:
    :return:
    """
    """
    传入码值后，
    1.在production_materials_similar查询码值，如果存在，则投料数量=剩余可用数量，结束，否则下一步；
    2.在production_materials中查询码值，如果存在记录，则结束，投料数量不变，否则进入下一步；
    3.判断码值是否有6对“&&”，有则进入解码过程，否则提示码值在MES中不存在。
    """
    lot_no_avaliable_num_map: dict = await get_material_code_num_map_new(code_list=[lot_no])
    if lot_no_avaliable_num_map:
        # 如果投料码是在production_materials_similar表里找到（分切工序的出站码），那投料数量不用入参传入的投料数量，
        # 而直接把production_materials_similar表里该条码的available_num投完， 投完后production_materials_similar表里的available_num变成0。
        # 但是如果本身available_num已经是<=0，就正常报错
        qty = lot_no_avaliable_num_map[lot_no].get('available_num', 0)
    else:
        lot_no_avaliable_num_map: dict = await get_material_code_num_map(code_list=[lot_no])
        if not lot_no_avaliable_num_map:
            if len(re.findall(r'&&', lot_no)) != 6:
                return False, f"原材料批次码:{lot_no}不存在MES系统中;", "", "", qty
            else:
                result, info, qr_code = await send_lot_no_mq(lot_no)
                if not result:
                    return result, info, "", "", qty
                else:
                    lot_no_avaliable_num_map: dict = await get_material_code_num_map(code_list=[qr_code])
                    lot_no = qr_code
    available_num = lot_no_avaliable_num_map.get(lot_no, {}).get("available_num", 0)
    get_logger().info(f"lot_no_avaliable_num_map:{lot_no_avaliable_num_map}, available_num:{available_num}, qty:{qty}")
    if available_num <= 0 or available_num < qty:
        info = f"条码{lot_no}剩余可投数量不足，当前剩余可投数量为{available_num};"
        get_logger().info(info)
        return False, info, "", "", qty

    material_code = lot_no_avaliable_num_map[lot_no]["material_code"]
    bom_info = await device_bom(device_code=equip_code)
    if bom_info:
        if bom_info.product_in:
            for rec in bom_info.product_in:
                if material_code == rec.material_code:
                    return True, "", material_code, lot_no, qty

    return False, "原材料批次码与当前订单工序BOM不匹配;", "", "", qty


async def update_and_insert_data(lot_no, qty, data):
    """
    登记当前投料蓝胶码/电芯码
    :param qty:
    :param lot_no:
    :param data:
    :return:
    """
    # is_feeding=True时，证明该批次码已经投过料，初始化时该配置表行记录没有这个字段，当原料登出后，该字段的值会更新为False
    query_filter = {"qr_code": lot_no}
    update_content = {
        "$inc": {"available_num": -1 * qty},
        "$set": {"is_feeding": True}  # 标记批次码处于投料状态
    }
    production_materials_collection = get_batch_code_config_collection()
    production_materials_collection_similar = get_new_batch_code_config_collection()
    check_document_1 = await production_materials_collection.find_one(query_filter)
    if check_document_1:
        await production_materials_collection_similar.find_one_and_update(query_filter, update_content)
    else:
        check_document_2 = await production_materials_collection.find_one(query_filter)
        if check_document_2:
            await production_materials_collection.find_one_and_update(query_filter, update_content)
    input_raw_collection = get_feeding_config_collection()
    await input_raw_collection.insert_one(data)


async def send_lot_no_mq(lot_no: str) -> Tuple[bool, str, str]:
    """
    以mq消息的形式将信息发送给 生产物料管理 app
    :param lot_no:
    :return:
    """
    # 2024-10-14, 新增需求：现在存在原料导入表、分切出站后生成的一张表，分别在两张表查询这个码是否存在，
    # 都不存在的时候，再增加条码解析的场景。具体的解析逻辑在郑宇开发的原材料信息导入app中，
    qr_code = ""
    message = {
        "code": lot_no,
        "time": get_time()
    }
    # 蓝京环境地址
    # url = "http://47.99.135.166:41020/lanjing/api/production_material_mgm/create_config_item"
    # 武汉生产测试环境地址
    # url = "http://223.76.187.243:8888/mes/api/production_material_mgm/create_config_item"

    url = "http://localhost:15937/api/production_material_mgm/create_config_item"
    result, data, info = await send_http_msg(msg=message, url=url)
    if result:
        qr_code = data.get("config_doc", {}).get("qr_code", "")
        info = f"原材料批次码origin_lot_no:{lot_no}不存在MES系统中, 现已录入lot_no:{qr_code}！"
    get_logger().info(info)
    return result, info, qr_code
